package pay.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.common.domain.CommonResult;
import com.common.domain.MyException;
import com.common.domain.PageResult;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import pay.config.PayClient;
import pay.config.PayClientFactory;
import pay.enums.*;
import pay.mapper.PayOrderExtensionMapper;
import pay.mapper.PayOrderMapper;
import pay.mapper.PayRefundMapper;
import pay.module.PayOrderDO;
import pay.module.channel.PayAppDO;
import pay.module.channel.PayChannelDO;
import pay.module.convent.PayOrderConvert;
import pay.module.notify.PayNotifyReqDTO;
import pay.module.notify.PayNotifyTaskCreateReqDTO;
import pay.module.notify.PayOrderNotifyRespDTO;
import pay.module.order.*;
import pay.module.order.vo.PayOrderExportReqVO;
import pay.module.order.vo.PayOrderPageReqVO;
import pay.module.order.vo.PayOrderSubmitReqVO;
import pay.module.order.vo.PayOrderSubmitRespVO;
import pay.module.refund.PayRefundDO;
import pay.service.*;

import com.common.enums.PayOrderStatusRespEnum;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static cn.hutool.core.util.ObjectUtil.notEqual;


/**
 * 支付订单 Service 实现类
 *
 * @author aquan
 */
@Service
@Validated
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class PayOrderServiceImpl implements PayOrderService {

    private final  static String CHANNEL_ID ="21";
    @Resource
    private PayProperties payProperties;

    @Resource
    private PayClientFactory payClientFactory;

    @Resource
    private PayOrderMapper orderMapper;
    @Resource
    private PayOrderExtensionMapper orderExtensionMapper;

    @Resource
    private PayChannelService channelService;
    @Resource
    private PayNotifyService notifyService;

    @Resource
    private PayAppService appService;


    @Resource
    @Lazy
    private PayOrderApi payOrderApi;



    @Resource
    private PayRefundMapper payRefundMapper;


    @Override
    public PayOrderDO getOrder(String id) {
        return orderMapper.selectById(id);
    }

    @Override
    public PageResult<PayOrderDO> getOrderPage(PayOrderPageReqVO pageReqVO) {
        return orderMapper.selectPage(pageReqVO);
    }

    @Override
    public List<PayOrderDO> getOrderList(PayOrderExportReqVO exportReqVO) {
        return orderMapper.selectList(exportReqVO);
    }

    @Override
    public List<PayOrderDO> getOrderListOne(PayOrderExportReqVO exportReqVO) {
        return orderMapper.selectListOne(exportReqVO);
    }


    @Override
    public List<PayOrderDO> getOrderSubjectList(Collection<Long> idList) {
        return orderMapper.findByIdListQueryOrderSubject(idList);
    }

    @Override
    public String createPayOrder(PayOrderCreateReqDTO reqDTO) {

        // 校验 App
        PayAppDO app = appService.validPayApp(reqDTO.getAppId());
        // 查询对应的支付交易单是否已经存在。如果是，则直接返回
        PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId(
                reqDTO.getAppId(), reqDTO.getMerchantOrderId());
        if (order != null) {
            log.warn("[createPayOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(),
                    order.getMerchantOrderId(), toJsonString(order)); // 理论来说，不会出现这个情况
            return order.getId();
        }

        // 创建支付交易单
        order = PayOrderConvert.INSTANCE.convert(reqDTO)
                .setMerchantId(app.getMerchantId()).setAppId(app.getId());
        // 商户相关字段
        order.setNotifyUrl(app.getPayNotifyUrl())
                .setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus());
        // 订单相关字段
        order.setStatus(PayOrderStatusEnum.WAITING.getStatus());
        // 退款相关字段
        order.setRefundStatus(PayOrderNotifyStatusEnum.NO.getStatus())
                .setRefundTimes(0).setRefundAmount(0L);
        order.setId(IdUtil.objectId());
        order.setCreator(StpUtil.getLoginId().toString());
        orderMapper.insert(order);
        // 最终返回
        return order.getId();
    }

    @Override
    public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) {
        // 1. 获得 PayOrderDO ，并校验其是否存在
        PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId());
        // 1.2 校验支付渠道是否有效
        PayChannelDO channel = validatePayChannelCanSubmit(order.getAppId(), reqVO.getChannelCode());
        PayClient client = payClientFactory.getPayClient(channel.getId());

        // 2. 插入 PayOrderExtensionDO
        PayOrderExtensionDO orderExtension = PayOrderConvert.INSTANCE.convert(reqVO, userIp)
                .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
                .setChannelId(channel.getId()).setChannelCode(channel.getCode())
                .setStatus(PayOrderStatusEnum.WAITING.getStatus());
        order.setCreator(StpUtil.getLoginId().toString());
        order.setId(IdUtil.objectId());
        orderExtensionMapper.insert(orderExtension);

        // 3. 调用三方接口
        PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO)
                .setBody("报告支付")
                // 商户相关的字段
                .setMerchantOrderId(orderExtension.getNo()) // 注意，此处使用的是 PayOrderExtensionDO.no 属性！
                .setSubject(order.getSubject())
                ///.setNotifyUrl(genChannelPayNotifyUrl(channel))
                .setNotifyUrl(genChannelPayNotifyUrl(channel))
                .setUserIp(order.getUserIp())
                // 订单相关字段
                .setAmount(order.getAmount()).setExpireTime(order.getExpireTime());
        PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);

        // 轮询三方接口，是否已经支付的任务
        // 返回成功
        return PayOrderConvert.INSTANCE.convert(unifiedOrderRespDTO);
    }

    private PayOrderDO validatePayOrderCanSubmit(String id) {
        PayOrderDO order = orderMapper.selectById(id);
        // 是否存在
        if (order == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        // 校验状态，必须是待支付
        if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getMsg());
        }
        // 校验是否过期
        if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) {
            throw new MyException(ErrorCodeConstants.ORDER_IS_EXPIRED.getCode(),ErrorCodeConstants.ORDER_IS_EXPIRED.getMsg());
        }
        // 【重要】校验是否支付拓展单已支付，只是没有回调、或者数据不正常
        validateOrderActuallyPaid(id);

        return order;
    }

    /**
     * 校验支付订单实际已支付
     *
     * @param id 支付编号
     */
   @VisibleForTesting
    void validateOrderActuallyPaid(String id) {
        List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByOrderId(id);
        orderExtensions.forEach(orderExtension -> {
            // 情况一：校验数据库中的 orderExtension 是不是已支付
            if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) {
                log.warn("[validateOrderCanSubmit][order({}) 的 extension({}) 已支付，可能是数据不一致]",
                        id, orderExtension.getId());
                throw new MyException(ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getCode(),ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getMsg());

            }
            // 情况二：调用三方接口，查询支付单状态，是不是已支付
            PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
            if (payClient == null) {
                log.error("[validateOrderCanSubmit][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
                return;
            }
            PayOrderRespDTO respDTO = payClient.getOrder(orderExtension.getNo());
            if (respDTO != null && PayOrderStatusRespEnum.isSuccess(respDTO.getStatus())) {
                log.warn("[validateOrderCanSubmit][order({}) 的 PayOrderRespDTO({}) 已支付，可能是回调延迟]",
                        id, toJsonString(respDTO));
                throw new MyException(ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getCode(),ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getMsg());
            }
        });
    }

    private PayChannelDO validatePayChannelCanSubmit(String appId, String channelCode) {
        // 校验 App
       // appService.validPayApp(appId);

        // 校验支付渠道是否有效
        PayChannelDO channel = channelService.validPayChannel(appId, channelCode);
        // 校验支付客户端是否正确初始化
        PayClient client = payClientFactory.getPayClient(channel.getId());
        if (client == null) {
            log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
            throw new MyException(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND.getMsg());
        }
        return channel;
    }

    /**
     * 根据支付渠道的编码，生成支付渠道的返回地址
     * @param channel 支付渠道
     * @return 支付成功返回的地址。 配置地址 + "/" + channel id
     */
    private String genChannelReturnUrl(PayChannelDO channel) {
        return payProperties.getReturnUrl() + "/" + channel.getId();
    }

    /**
     * 根据支付渠道的编码，生成支付渠道的回调地址
     *
     * @param channel 支付渠道
     * @return 支付渠道的回调地址  配置地址 + "/" + channel id
     */
    private String genChannelPayNotifyUrl(PayChannelDO channel) {
        return payProperties.getCallbackUrl() + "/" + channel.getId();
    }

    private String generateOrderExtensionNo() {
//    wx
//    2014
//    10
//    27
//    20
//    09
//    39
//    5522657
//    a690389285100
        // 目前的算法
        // 时间序列，年月日时分秒 14 位
        // 纯随机，6 位 此处估计是会有问题的，后续在调整
        return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列
                RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围，因为偷懒
                ;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notifyPayOrder(String channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) {
        // 校验支付渠道是否有效
        PayChannelDO channel = channelService.validPayChannel(channelId);
            // 1. 更新 PayOrderExtensionDO 支付成功
            PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(),
                    rawNotify);
            // 2. 更新 PayOrderDO 支付成功
            PayOrderDO order = updatePayOrderSuccess(channel, orderExtension, notify);
            // 3. 插入支付通知记录
            notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
                    .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
    }

    @Override
    public PayOrderRespDTO queryOtherOrder(String orderNumber) {
        //暂时写死
        String channelId = CHANNEL_ID;
        // 情况二：调用三方接口，查询支付单状态，是不是已支付
        PayClient payClient = payClientFactory.getPayClient(channelId);
        if (payClient == null) {
            log.error("[validateOrderCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channelId);
            return null;
        }
        PayOrderRespDTO respDTO = payClient.getOrder(orderNumber);
//        System.out.println("respDTO = " + respDTO.toString());
//        if (respDTO != null && PayOrderStatusRespEnum.isSuccess(respDTO.getStatus())) {
//            log.warn("[validateOrderCanSubmit][order({}) 的 PayOrderRespDTO({}) 已支付，可能是回调延迟]",
//                    orderNumber, toJsonString(respDTO));
//            throw new MyException(ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getCode(),ErrorCodeConstants.ORDER_EXTENSION_IS_PAID.getMsg());
//        }
        return respDTO;
    }

    /**
     * 更新 PayOrderExtensionDO 支付成功
     *
     * @param no 支付订单号（支付模块）
     * @param rawNotify 通知数据
     * @return PayOrderExtensionDO 对象
     */
    private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) {
        // 1.1 查询 PayOrderExtensionDO
        PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
        if (orderExtension == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getMsg());
        }
        // 校验状态，必须是待支付
        if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }
        // 1.2 更新 PayOrderExtensionDO
        PayOrderExtensionDO build = PayOrderExtensionDO.builder()
                .status(PayOrderStatusEnum.SUCCESS.getStatus())
                .channelNotifyData(toJsonString(rawNotify)).build();
        build.setId(orderExtension.getId());
        build.setUpdateTime(LocalDateTime.now());
        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
                PayOrderStatusEnum.WAITING.getStatus(),build);
        // 校验状态，必须是待支付
        if (updateCounts == 0) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }
        log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
        return orderExtension;
    }

    /**
     * 更新 PayOrderDO 支付成功
     *
     * @param channel 支付渠道
     * @param orderExtension 支付拓展单
     * @param notify 通知回调
     * @return PayOrderDO 对象
     */
    private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
                                             PayOrderNotifyRespDTO notify) {
        // 2.1 判断 PayOrderDO 是否处于待支付
        PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
        if (order == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) {
            // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getMsg());
        }
        // 2.2 更新 PayOrderDO
        int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
                PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus())
                        .channelId(channel.getId()).channelCode(channel.getCode())
                        .successTime(notify.getSuccessTime()).successExtensionId(orderExtension.getId())
                        .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId())
                        .notifyTime(LocalDateTime.now()).build());
        // 校验状态，必须是待支付
        if (updateCounts == 0) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getMsg());
        }
//        //修改订单状态
//        List<ReportOrder> list = reportOrderMapper.selectList(new QueryWrapperX<ReportOrder>().eq("pay_order_id", order.getId()));
//        if(CollectionUtils.isNotEmpty(list)){
//            ReportOrder reportOrder = list.get(0);
//            reportOrder.setOrderStatus(PayOrderStatusRespEnum.SUCCESS.getStatus());
//            reportOrderMapper.updateByOrderId(reportOrder.getOrderNumber(),reportOrder);
//        }
//        log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId());
        return order;
    }

    @Override
    public void updateDemoOrderPaid(String id, String payOrderId) {
//        // 校验并获得支付订单（可支付）
//        PayOrderRespDTO payOrder = this.validateDemoOrderCanPaid(id, payOrderId);
//        // 更新 PayDemoOrderDO 状态为已支付
//        int updateCount = reportOrderMapper.updateByIdAndTime(id, PayOrderStatusRespEnum.WAITING.getStatus(),
//                new ReportOrder().setOrderStatus(PayOrderStatusRespEnum.SUCCESS.getStatus()).setNotifyTime(LocalDateTime.now()).setSuccessTime(LocalDateTime.now()));
//        if (updateCount == 0) {
//            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID.getMsg());
//        }
    }
    @Override
    public void updateRefundOrderPaid(String id, String payOrderId) {
        // 校验并获得退款订单
        PayRefundDO payOrder = this.validateOrderCanRefund(payOrderId);
        int updateCount = 0;
        if(Objects.equals(payOrder.getStatus(),PayRefundStatusEnum.FAILURE.getStatus())){
             updateCount = reportOrderMapper.updateByIdAndTime(id, PayOrderStatusRespEnum.REFUNDING.getStatus(),
                    new ReportOrder().setOrderStatus(PayOrderStatusRespEnum.REFUND_FAIL.getStatus()).setNotifyTime(LocalDateTime.now()));
        }else{
            // 更新 状态为已退款
             updateCount = reportOrderMapper.updateByIdAndTime(id, PayOrderStatusRespEnum.REFUNDING.getStatus(),
                    new ReportOrder().setOrderStatus(PayOrderStatusRespEnum.REFUND_SUCCESS.getStatus()).setNotifyTime(LocalDateTime.now()).setSuccessTime(LocalDateTime.now()));
        }
        if (updateCount == 0) {
            throw new MyException(ErrorCodeConstants.PAY_REFUND_FAIL.getCode(),ErrorCodeConstants.PAY_REFUND_FAIL.getMsg());
        }
    }
    /**
     * 校验交易订单满足被支付的条件
     *
     * 1. 交易订单未支付
     * 2. 支付单已支付
     *
     * @param id 交易订单编号
     * @param payOrderId 支付订单编号
     * @return 交易订单
     */
    private PayOrderRespDTO validateDemoOrderCanPaid(String id, String payOrderId) {
        // 1.1 校验订单是否存在
        ReportOrder reportOrder = new ReportOrder();
        reportOrder.setOrderNumber(id);
        List<ReportOrder> list = reportOrderMapper.selectList(reportOrder);
        if (CollectionUtils.isEmpty(list)) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        ReportOrder order = list.get(0);
        // 1.2 校验订单未支付
        if (!order.getOrderStatus().equals(PayOrderStatusRespEnum.WAITING.getStatus())) {
            log.error("[validateDemoOrderCanPaid][order({}) 不处于待支付状态，请进行处理！order 数据是：{}]",
                    id, toJsonString(order));
            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID.getMsg());
        }
        // 1.3 校验支付订单匹配
        // 支付单号
        if (notEqual(order.getPayOrderId(), payOrderId)) {
            log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({})，请进行处理！order 数据是：{}]",
                    id, payOrderId, toJsonString(order));
            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR.getMsg());
        }

        // 2.1 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 不存在，请进行处理！]", id, payOrderId);
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        // 2.2 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, toJsonString(payOrder));
            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS.getMsg());
        }
        // 2.3 校验支付金额一致
        if (notEqual(payOrder.getAmount(), order.getAmount())) {
            log.error("[validateDemoOrderCanPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
                    id, payOrderId, toJsonString(order), toJsonString(payOrder));
            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH.getMsg());
        }
        // 2.4 校验支付订单匹配（二次）
        if (notEqual(payOrder.getMerchantOrderId(), id)) {
            log.error("[validateDemoOrderCanPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, toJsonString(payOrder));
            throw new MyException(ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR.getCode(),ErrorCodeConstants.PAY_ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR.getMsg());
        }
        return payOrder;
    }
    /**
     * 同步单个支付单
     *
     * @param order 支付单
     * @return 是否已过期
     */
    @Override
    public boolean expireOrder(PayOrderDO order) {
        try {
            // 1. 需要先处理关联的支付拓展单，避免错误的过期已支付 or 已退款的订单
            List<PayOrderExtensionDO> orderExtensions = orderExtensionMapper.selectListByOrderId(order.getId());
            if(CollectionUtils.isNotEmpty(orderExtensions)) {
                for (PayOrderExtensionDO orderExtension : orderExtensions) {
                    if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) {
                        continue;
                    }
                    // 情况一：校验数据库中的 orderExtension 是不是已支付
                    if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) {
                        log.error("[expireOrder][order({}) 的 extension({}) 已支付，可能是数据不一致]",
                                order.getId(), orderExtension.getId());
                        return false;
                    }
                    //默认都是一种情况查询 微信小程序支付 channelid =CHANNEL_ID
                    // 情况二：调用三方接口，查询支付单状态，是不是已支付/已退款
                    PayClient payClient = payClientFactory.getPayClient(CHANNEL_ID);
                    if (payClient == null) {
                        log.error("[expireOrder][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
                        return false;
                    }
                    PayOrderRespDTO respDTO = payClient.getOrder(orderExtension.getNo());
                    if (PayOrderStatusRespEnum.isRefund(respDTO.getStatus())) {
                        // 补充说明：按道理，应该是 WAITING => SUCCESS => REFUND 状态，如果直接 WAITING => REFUND 状态，说明中间丢了过程
                        // 此时，需要人工介入，手工补齐数据，保持 WAITING => SUCCESS => REFUND 的过程
                        log.error("[expireOrder][extension({}) 的 PayOrderRespDTO({}) 已退款，可能是回调延迟]",
                                orderExtension.getId(), toJsonString(respDTO));
                        return false;
                    }
                    //未支付的默认按微信小程序支付渠道支付
                    PayChannelDO channel = channelService.getChannel(CHANNEL_ID);
                    if (PayOrderStatusRespEnum.isSuccess(respDTO.getStatus())) {
                        notifyOrder(channel, respDTO);
                        return false;
                    }
                    // 兜底逻辑：将支付拓展单更新为已关闭
                    PayOrderExtensionDO updateObj = new PayOrderExtensionDO().setStatus(PayOrderStatusEnum.CLOSED.getStatus())
                            .setChannelNotifyData(toJsonString(respDTO));
                    if (orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(),
                            updateObj) == 0) {
                        log.error("[expireOrder][extension({}) 更新为支付关闭失败]", orderExtension.getId());
                        return false;
                    }
                    log.info("[expireOrder][extension({}) 更新为支付关闭成功]", orderExtension.getId());
                }
            }
            // 2. 都没有上述情况，可以安心更新为已关闭
            PayOrderDO updateObj = new PayOrderDO().setStatus(PayOrderStatusEnum.CLOSED.getStatus());
            ReportOrder reportOrder = new ReportOrder().setOrderStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
            int i = reportOrderMapper.updateByPayOrderId(order.getId(), reportOrder);
            if (orderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateObj) == 0) {
                log.error("[expireOrder][order({}) 更新为支付关闭失败]", order.getId());
                return false;
            }
            if (i == 0) {
                log.error("[expireOrder][reportOrder({}) 更新为支付关闭失败]", order.getId());
                return false;
            }
            log.info("[expireOrder][order({}) 更新为支付关闭失败]", order.getId());
            return true;
        } catch (Throwable e) {
            log.error("[expireOrder][order({}) 过期订单异常]", order.getId(), e);
            return false;
        }
    }

    @Override
    public CommonResult cancelOrder(String orderNumber) {
        List<ReportOrder> orderList = reportOrderMapper.selectList("order_number", orderNumber);
        if(CollectionUtils.isEmpty(orderList)){
            throw new MyException(ErrorCodeConstants.ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.ORDER_NOT_FOUND.getMsg());
        }
        ReportOrder reportOrder = orderList.get(0);
        PayOrderDO payOrderDO = orderMapper.selectById(reportOrder.getPayOrderId());
        if(Objects.isNull(payOrderDO)){
            throw  new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        boolean b = this.expireOrder(payOrderDO);
        return b?CommonResult.success("取消订单成功"):CommonResult.failure("取消订单失败！请联系管理员处理");

    }

    @Transactional(rollbackFor = Exception.class)
    // 注意，如果是方法内调用该方法，需要通过 getSelf().notifyPayOrder(channel, notify) 调用，否则事务不生效
    public void notifyOrder(PayChannelDO channel, PayOrderRespDTO notify) {
        // 情况一：支付成功的回调
        if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) {
            notifyOrderSuccess(channel, notify);
            return;
        }
        // 情况二：支付失败的回调
        if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) {
            notifyOrderClosed(channel, notify);
        }
        // 情况三：WAITING：无需处理
        // 情况四：REFUND：通过退款回调处理
    }

    private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
        // 1. 更新 PayOrderExtensionDO 支付成功
        PayOrderExtensionDO orderExtension = updateOrderSuccess(notify);
        // 2. 更新 PayOrderDO 支付成功
        Boolean paid = updateOrderSuccess(channel, orderExtension, notify);
        if (paid) { // 如果之前已经成功回调，则直接返回，不用重复记录支付通知记录；例如说：支付平台重复回调
            return;
        }

        // 3. 插入支付通知记录
        PayNotifyTaskCreateReqDTO reqDTO = new PayNotifyTaskCreateReqDTO();
        reqDTO.setDataId(orderExtension.getOrderId());
        reqDTO.setType(PayNotifyTypeEnum.ORDER.getType());
        notifyService.createPayNotifyTask(reqDTO);
    }

    /**
     * 更新 PayOrderExtensionDO 支付成功
     *
     * @param notify 通知
     * @return PayOrderExtensionDO 对象
     */
    private PayOrderExtensionDO updateOrderSuccess(PayOrderRespDTO notify) {
        // 1. 查询 PayOrderExtensionDO
        PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
        if (orderExtension == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getMsg());
        }
        if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功，直接返回，不用重复更新
            log.info("[updateOrderExtensionSuccess][orderExtension({}) 已经是已支付，无需更新]", orderExtension.getId());
            return orderExtension;
        }
        if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }

        // 2. 更新 PayOrderExtensionDO
        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
                PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build());
        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }
        log.info("[updateOrderExtensionSuccess][orderExtension({}) 更新为已支付]", orderExtension.getId());
        return orderExtension;
    }

    /**
     * 更新 PayOrderDO 支付成功
     *
     * @param channel        支付渠道
     * @param orderExtension 支付拓展单
     * @param notify         通知回调
     * @return 是否之前已经成功回调
     */
    private Boolean updateOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
                                       PayOrderRespDTO notify) {
        // 1. 判断 PayOrderDO 是否处于待支付
        PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
        if (order == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功，直接返回，不用重复更新
                && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
            log.info("[updateOrderExtensionSuccess][order({}) 已经是已支付，无需更新]", order.getId());
            return true;
        }
        if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getMsg());
        }
        // 2. 更新 PayOrderDO
        int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
                PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus())
                        .channelId(channel.getId()).channelCode(channel.getCode())
                        .successTime(notify.getSuccessTime()).successExtensionId(orderExtension.getId()).merchantOrderId(orderExtension.getNo())
                        .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId())
                        .channelFeeRate(channel.getFeeRate())
                        .channelFeeAmount(MoneyUtils.calculateRatePrice(order.getAmount(), channel.getFeeRate()))
                        .build());
        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING.getMsg());
        }
        log.info("[updateOrderExtensionSuccess][order({}) 更新为已支付]", order.getId());
        return false;
    }

    private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) {
        updateOrderExtensionClosed(channel, notify);
    }

    private void updateOrderExtensionClosed(PayChannelDO channel, PayOrderRespDTO notify) {
        // 1. 查询 PayOrderExtensionDO
        PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
        if (orderExtension == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND.getMsg());
        }
        if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // 如果已经是关闭，直接返回，不用重复更新
            log.info("[updateOrderExtensionClosed][orderExtension({}) 已经是支付关闭，无需更新]", orderExtension.getId());
            return;
        }
        // 一般出现先是支付成功，然后支付关闭，都是全部退款导致关闭的场景。这个情况，我们不更新支付拓展单，只通过退款流程，更新支付单
        if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) {
            log.info("[updateOrderExtensionClosed][orderExtension({}) 是已支付，无需更新为支付关闭]", orderExtension.getId());
            return;
        }
        if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }

        // 2. 更新 PayOrderExtensionDO
        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
                PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify))
                        .channelNotifyData(notify.getChannelErrorMsg()+notify.getChannelErrorCode()).build());
        if (updateCounts == 0) { // 校验状态，必须是待支付
            throw new MyException(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getCode(),ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING.getMsg());
        }
        log.info("[updateOrderExtensionClosed][orderExtension({}) 更新为支付关闭]", orderExtension.getId());
    }
    private PayRefundDO validateOrderCanRefund(String id) {
        // 校验订单是否存在
        PayRefundDO order = payRefundMapper.selectById(id);
        if (order == null) {
            throw new MyException(ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getCode(),ErrorCodeConstants.PAY_ORDER_NOT_FOUND.getMsg());
        }
        return order;
    }

}
